package core.service
{
import cmodule.atlantis.CLibInit;

import com.adobe.crypto.MD5;
import com.adobe.serialization.json.JSON;
import com.hurlant.util.Base64;

import core.Configuration;

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.utils.clearTimeout;
import flash.utils.setTimeout;

import framework.utils.SecureUtil;

import utils.TimeUtil;

/**
 * ReleaseService
 * @author jliang
 *
 */
public class ReleaseService extends EventDispatcher implements IService
{
	//==========================================================================
	//  Class constants
	//==========================================================================
	public static const REQUEST_STAMP_MAX:Number = 9999999;
	
	public static const REQUEST_TIMEOUT:Number = 300000;
	
	public static const MAX_RETRY_COUNT:Number = 3;
	
	private static var signKey:String;
	
	private static var secureKey:String;
	//==========================================================================
	//  Constructor
	//==========================================================================
	public function ReleaseService()
	{
		super();
		
		var lib:Object = new CLibInit().init();
		signKey = lib.getSignCode();
		secureKey = lib.getSecureCode();
	}
	//==========================================================================
	//  Protected methods
	//==========================================================================
	private var retryCount:int = 0;
	
	private var requestStamp:Number = 0;
	
	private var queue:Vector.<Array> = new Vector.<Array>();   /* Array.<ServiceRequest> */
	
	private var forceStop:Boolean = false;
	
	private var timer:uint;
	//==========================================================================
	//  Public methods
	//==========================================================================
	public function invoke(request:ServiceRequest, ...args):void
	{
		queue.push([ request ].concat(args));
		
		if (queue.length == 1) process();
	}
	//==========================================================================
	//  Private methods
	//==========================================================================
	private function process():void
	{		
		if (forceStop || queue.length == 0) return;
		
		var requests:Array = queue[0];
		
		var params:Array = [];
		
		//set the general info
		params.push( [ Configuration.socialID, requestStamp, TimeUtil.currentGameTime.time, {} ] );
		
		//set the requests
		params.push( [  ] );
		
		for (var i:int = 0; i < requests.length; i++)
		{
			params[1].push(ServiceRequest(requests[i]).gameRequestParamFormat);
		}
		
		var request:URLRequest = new URLRequest();
		request.url = Configuration.gatewayPath;
		request.method = URLRequestMethod.POST;
		request.contentType = "application/octet-stream";
		
		var obj:Object = new Object();
		obj["data"] = JSON.encode(params);
		obj["sid"] = Configuration.sid;
		obj["ver"] = Configuration.version;
		obj["sig"] = MD5.hash(signKey + String(obj["data"]));
        
        trace("*** Send to server: \n" + obj.data + "\n");
		
		request.data = Base64.decodeToByteArray(SecureUtil.encrypt(JSON.encode(obj), secureKey));
		
		var loader:URLLoader = new URLLoader();
		loader.dataFormat = URLLoaderDataFormat.BINARY;
		loader.addEventListener(Event.COMPLETE, completeHandler);
		loader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
		loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler);
		
		startTimeout(loader);
		
		loader.load(request);
	}
	
	private function updateStamp():void
	{
		if (requestStamp >= REQUEST_STAMP_MAX)
		{
			requestStamp = 0;
		}
		else
		{
			requestStamp ++;
		}
	}
	
	private function startTimeout(loader:URLLoader):void
	{
		stopTimeout();
		
		timer = setTimeout(timeoutHandler, REQUEST_TIMEOUT, loader);
	}
	
	private function stopTimeout():void
	{
		clearTimeout(timer);
	}
	
	private function retry():void
	{
		retryCount ++;
		
		//如果超过重试次数
		if (retryCount > MAX_RETRY_COUNT)
		{			
			var requests:Array = queue[0];
			
			for (var i:int = 0; i < requests.length; i++)
			{
				var request:ServiceRequest = requests[i];				
				request.error = new ErrorResponse({ method:request.method, message:"-1" });				
				request.complete();
			}
			
			forceStop = true;
			return;
		}
		
		process();
	}
	//==========================================================================
	//  Event handlers
	//==========================================================================
	private function completeHandler(event:Event):void
	{
		updateStamp();
		
		retryCount = 0;
		
		var loader:URLLoader = event.target as URLLoader;
		loader.removeEventListener(Event.COMPLETE, completeHandler);
		loader.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
		loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler);
        
		var obj:Object;	
		var errorID:String;		
		var results:Array;
        
        var dataStr:* = SecureUtil.decrypt(loader.data, secureKey);
        
        trace("*** Get response from server: \n" + dataStr + "\n");
        
        try
        {
            obj = JSON.decode(String(dataStr));
        }
        catch (error:Error)
        {
            obj = String(dataStr);
        }
		
		//如果请求发生错误
		if (obj.hasOwnProperty("err_id"))
		{
			errorID = obj.err_id;
			
			if (obj.hasOwnProperty("stop"))
			{
				forceStop = (int(obj.stop) == 1 ? true : false);
			}
		}
		else
		{
			results = obj.result;
			
			forceStop = false;
		}
		
		var requests:Array = queue[0];
		
		for (var i:int = 0; i < requests.length; i++)
		{
			var request:ServiceRequest = requests[i];
			
			if (errorID)
			{
				request.error = new ErrorResponse({ method:request.method, message:obj.err_id });
			}
			else
			{
				request.response = new SuccessResponse(results[i].data);
			}
			
			request.complete();
		}
		
		queue.shift();
		
		if (queue.length > 0) process();
	}
	
	private function errorHandler(event:Event):void
	{
		var loader:URLLoader = event.target as URLLoader;
		loader.removeEventListener(Event.COMPLETE, completeHandler);
		loader.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
		loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler);
		
		try
		{
			loader.close();
		}
		catch (error:Error)
		{
			//do nothing
		}
		
		retry();
	}
	
	private function timeoutHandler(loader:URLLoader):void
	{
		stopTimeout();
		
		loader.removeEventListener(Event.COMPLETE, completeHandler);
		loader.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
		loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler);
		
		try
		{
			loader.close();
		}
		catch (error:Error)
		{
			//do nothing
		}
		
		retry();
	}
}
}





